using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Collections;
using System.Threading;
using System.IO;
using System.Net;

namespace FileUploader
{
    #region uploader
    public class Uploader
    {
        //<tasks>
        //  <task name="contact.txt" percentage="" totalSeconds="" localFile="" remoteFile="" fileSize="">
        //    <thread name="thread1" begin="" end="" lastTime=""></thread>
        //  </task>
        //</tasks>
        private Task _task;
        public Task Task
        {
            get
            {
                return _task;
            }
        }

        public Uploader(Task task)
        {
            _task = task;
        }
        public void Strat()
        {
            //_dataReceiver.Open(this._task.RemoteFile);//init webservice
            #region test
            //if (!File.Exists(_task.RemoteFile))
            //{
            //    FileInfo fi = new FileInfo(_task.LocalFile);

            //    FileStream fst = new FileStream(_task.RemoteFile, FileMode.CreateNew);

            //    BinaryWriter w = new BinaryWriter(fst);
            //    byte[] ab = new byte[fi.Length];
            //    w.Write(ab);
            //    w.Close();
            //    fst.Close();
            //}
            // 
            #endregion
            //main code. find each fileChunk, running in seperate thread
            for (int i = 0; i < _task.FileChunks.Count; i++)
            {
                FileChunk fc = (_task.FileChunks[i] as FileChunk);
                Thread thread = new Thread(new ThreadStart(fc.Upload));
                fc.RunningThread = thread;//asign thread to the fileChucks, to stop the task
                thread.Name = _task.Name + "_" + i.ToString();
                thread.Start();
            }
        }

    } 
    #endregion
    #region task
    /// <summary>
    /// task defination
    /// </summary>
    public class Task
    {

        #region property
        public string Name;
        public string LocalFile;
        public string RemoteFile;

        public long FileSize;

        public int FileChunkCount;//how many block of this file

        /// <summary>
        /// get runing percentage
        /// </summary>
        public long Percentage;

        public int TotalSeconds;//running time

        /// <summary>
        /// webservice to upload files
        /// </summary>
        public FileExplorer.Explorer DataReceiver;//webservice, to receive the data

        public ArrayList FileChunks;

        private XmlNode XmlPersist;
        #endregion

        public Task()
        {

        }
        //test code, load task from xml
        public Task(XmlNode task, FileExplorer.Explorer dataReceiver)
        {
            XmlPersist = task;

            this.Name = XmlPersist.Attributes["name"].Value.ToString();
            this.LocalFile = XmlPersist.Attributes["localFile"].Value.ToString();
            this.RemoteFile = XmlPersist.Attributes["remoteFile"].Value.ToString();
            this.FileSize =Convert.ToInt32(XmlPersist.Attributes["fileSize"].Value) ;
            this.Percentage = Convert.ToInt32(XmlPersist.Attributes["percentage"].Value);
            this.TotalSeconds = Convert.ToInt32(XmlPersist.Attributes["totalSeconds"].Value);

            this.DataReceiver = dataReceiver;
            
             FileChunks = new ArrayList();
             for (int i = 0; i < XmlPersist.ChildNodes.Count; i++)
            {
                FileChunk fc = new FileChunk(this);
                fc.Begin = Convert.ToInt32(XmlPersist.ChildNodes[i].Attributes["begin"].Value);
                fc.End = Convert.ToInt32(XmlPersist.ChildNodes[i].Attributes["end"].Value);
                fc.LastTime = Convert.ToDateTime(XmlPersist.ChildNodes[i].Attributes["lastTime"].Value);
                FileChunks.Add(fc);
            }
            this.FileChunkCount = FileChunks.Count;
        }
        /// <summary>
        /// init to spilit files to pieces 
        /// </summary>
        public void Init()
        {
            //create new
            this.FileChunks = new ArrayList();

            FileInfo fi = new FileInfo(LocalFile);
            FileSize = fi.Length;

            long block = FileSize / this.FileChunkCount;
            long index = 0;
            for (int i = 0; i < this.FileChunkCount; i++)
            {
                FileChunk fc = new FileChunk(this);
                fc.Begin = index;
                if (i == this.FileChunkCount - 1)
                    block = FileSize - index;
                index += block;
                fc.End = index;
                fc.LastTime = DateTime.Now;

                this.FileChunks.Add(fc);
            }
            //
            StringBuilder sb = new StringBuilder();           
            sb.AppendLine(string.Format("<task name='{0}' percentage='0' totalSeconds='0' localFile='{1}' remoteFile='{2}' fileSize='{3}' server='{4}'>",fi.Name,fi.FullName,this.RemoteFile,fi.Length,this.DataReceiver.Url));
            for (int i = 0; i < this.FileChunks.Count; i++)
            {
                FileChunk fc=this.FileChunks[i] as FileChunk;
                sb.AppendLine(string.Format("  <thread name='thread{0}' begin='{1}' end='{2}' lastTime='{3}'></thread>", i, fc.Begin, fc.End, fc.LastTime.ToString("yyyy-MM-dd HH:mm:ss")));
            }
            sb.AppendLine("</task>");

            try
            {
                XmlDocument doc = new XmlDocument();
                doc.Load("tasks.xml");
                doc.LastChild.InnerXml = doc.LastChild.InnerXml + sb.ToString();
                doc.Save("tasks.xml");
                doc.Load("tasks.xml");
                XmlPersist = doc.LastChild.LastChild;
            }
            catch (Exception ex)
            { 
            
            }
        }

        public void Save()
        {
            if (FileSize == 0)
            {
                Init();
            }
            else
            {
                long totalLeft = 0;
                for (int i = 0; i < this.FileChunks.Count; i++)
                {
                    FileChunk fc = (this.FileChunks[i] as FileChunk);
                    totalLeft += fc.End - fc.Begin;
                }
                Percentage = ((FileSize - totalLeft) * 100) / FileSize;

            }

            //save to xml, test code
            //
            //<tasks>
            //  <task name="contact.txt" percentage="" totalSeconds="" localFile="" remoteFile="" fileSize="" server="">
            //    <thread name="thread1" begin="" end="" lastTime=""></thread>
            //  </task>
            //</tasks>
            try
            {
                XmlPersist.Attributes["percentage"].Value = this.Percentage.ToString();
                XmlPersist.Attributes["totalSeconds"].Value = this.TotalSeconds.ToString();
                for (int i = 0; i < this.FileChunks.Count; i++)
                {
                    XmlPersist.ChildNodes[i].Attributes["begin"].Value = (this.FileChunks[i] as FileChunk).Begin.ToString();
                    XmlPersist.ChildNodes[i].Attributes["end"].Value = (this.FileChunks[i] as FileChunk).End.ToString();
                    XmlPersist.ChildNodes[i].Attributes["lastTime"].Value = (this.FileChunks[i] as FileChunk).LastTime.ToString("yyyy-MM-dd HH:mm:ss");
                }
                File.WriteAllText("tasks.xml", XmlPersist.OwnerDocument.InnerXml);
            }
            catch (Exception ex)
            {

            }
            //

        }
        /// <summary>
        /// remot the task from list and xml , and kill the thread 
        /// </summary>
        public void Remove()
        {
            //
            if (FileChunks != null)
            {
                for (int i = 0; i < this.FileChunks.Count; i++)
                {
                    (FileChunks[i] as FileChunk).Stop();
                }
            }
 
            
            try
            {
                XmlPersist.OwnerDocument.LastChild.RemoveChild(XmlPersist);;
                File.WriteAllText("tasks.xml", XmlPersist.OwnerDocument.InnerXml);
            }
            catch (Exception ex)
            {

            }
         
        }
        /// <summary>
        /// load the pending task that belongs to this server
        /// </summary>
        /// <param name="dataReceiver"></param>
        /// <returns></returns>
        public static ArrayList LoadTasks(FileExplorer.Explorer dataReceiver)
        {
            //test code, the data source is  xml, LoadDataFromXml
            ArrayList result = new ArrayList();

            XmlDocument tasks = new XmlDocument();
            tasks.Load("tasks.xml");
            for (int i = 0; i < tasks.ChildNodes[1].ChildNodes.Count; i++)
            {
                if (tasks.LastChild.ChildNodes[i].Attributes["server"].Value == dataReceiver.Url)
                {
                    Task task = new Task(tasks.LastChild.ChildNodes[i], dataReceiver);
                    result.Add(task);
                }

            }
           

            return result;
        }

    } 
    #endregion
    #region filechunk
    /// <summary>
    /// file chunk infomation
    /// will bind to thread to run in seperate space
    /// </summary>
    public class FileChunk
    {
        public long Begin;//postion of the begin
        public long End;
        public DateTime LastTime; //last update date time

        public Thread RunningThread;//running thread

        private Task _task; // parent task

        public FileChunk(Task task)
        {
            _task = task;
        }
        public void Stop()
        {
            if (RunningThread != null)
            {
                RunningThread.Abort();
            }
        }
        /// <summary>
        /// main function
        /// </summary>
        public void Upload()
        {
            Stream fs = File.OpenRead(_task.LocalFile);

            long len = 2 * 64 * 1024;// 2 * 64 * 1024;

            while (Begin < End)
            {
                if (End - Begin < len)
                    len = End - Begin;
                byte[] data = new byte[len];
                fs.Position = Begin;
                fs.Read(data, 0, (int)len);

                #region local test
                //Stream fs2 = File.OpenWrite(_task.RemoteFile);
                //fs2.Position = Begin;
                //fs2.Write(data, 0, (int)len);
                //fs2.Flush();
                //fs2.Close(); 
                //
                #endregion

                //call webservice to receive the data. this can be a socket also.
                string res = this._task.DataReceiver.Receive(_task.RemoteFile, Begin, len, data);
                if (res == "ok")
                {
                    //
                    Begin += len;
                    _task.Save();
                }
                else
                {
                    //wait for next while, 
                }

            }
        }
    } 
    #endregion
}